
#pragma once

#include "SocketHelper.h"
#include "../PrivateHeap.h"
#include "../Event.h"
#include "../Semaphore.h"
#include "../CriticalSection.h"
#include "../STLHelper.h"


class CIocpServer : public ISocketServer
{
public:
	static const long	DEFAULT_IOCP_THREAD_COUNT;
	static const long	DEFAULT_ACCEPT_SOCKET_COUNT;
	static const long	DEFAULT_IOCP_BUFFER_SIZE;
	static const long	DEFAULT_SOCKET_LISTEN_QUEUE;
	static const long	DEFAULT_FREE_SOCKETOBJ_POOL;
	static const long	DEFAULT_FREE_BUFFEROBJ_POOL;
	static const long	DEFAULT_FREE_SOCKETOBJ_HOLD;
	static const long	DEFAULT_FREE_BUFFEROBJ_HOLD;
	static const long	DEFALUT_KEEPALIVE_TIMES;
	static const long	DEFALUT_KEEPALIVE_INTERVAL;

public:
	CIocpServer(IServerSocketListener* pListener)
		: m_psoListener(pListener)
		, m_hAcceptThread(NULL)
		, m_hCompletePort(NULL)
		, m_soListen(INVALID_SOCKET)
		, m_pfnAcceptEx(NULL)
		, m_pfnGetAcceptExSockaddrs(NULL)
		, m_enLastError(ISS_OK)
		, m_bStarted(FALSE)
		, m_semAccept(DEFAULT_IOCP_THREAD_COUNT, DEFAULT_IOCP_THREAD_COUNT)
	{
		ASSERT(m_wsSocket.IsValid());
		ASSERT(m_psoListener);

		Reset();
	}

	virtual ~CIocpServer()
	{
		if(HasStarted())
			Stop();
	}

public:
	virtual BOOL Start	(LPCTSTR pszBindAddress, USHORT usPort, long lThreadCount = DEFAULT_IOCP_THREAD_COUNT);
	virtual BOOL Stop	();
	virtual BOOL Send	(DWORD dwConnID, const BYTE* pBuffer, int iLen);
	virtual BOOL			HasStarted		()	{return m_bStarted;}
	virtual En_ISS_Error	GetLastError	()	{return m_enLastError;}
	virtual LPCTSTR			GetLastErrorDesc();
	virtual BOOL			GetConnectionAddress(DWORD dwConnID, CString& strAddress, USHORT& usPort);

private:
	void SetLastError(En_ISS_Error code, LPCTSTR func, int ec);

private:
	BOOL CreateListenSocket(LPCTSTR pszBindAddress, USHORT usPort);
	BOOL CreateCompletePort();
	BOOL CreateWorkerThreads(long lThreadCount);
	BOOL StartAcceptThread();

	void CloseListenSocket();
	void WaitForAcceptThreadEnd();
	void CloseClientSocket();
	void ReleaseFreeSocket();
	void CompressFreeSocket(size_t size);
	void ReleaseFreeBuffer();
	void CompressFreeBuffer(size_t size);
	void WaitForWorkerThreadEnd();
	void TerminateWorkerThread();
	void CloseCompletePort();

	void Reset();

	TBufferObj*	GetFreeBufferObj(int iLen = DEFAULT_IOCP_BUFFER_SIZE);
	TSocketObj*	GetFreeSocketObj();
	void		AddFreeBufferObj(TBufferObj* pBufferObj);
	void		AddFreeSocketObj(DWORD dwConnID, BOOL bClose = TRUE, BOOL bGraceful = TRUE, BOOL bReuseAddress = FALSE);
	TBufferObj*	CreateBufferObj();
	TSocketObj*	CreateSocketObj();
	void		DeleteBufferObj(TBufferObj* pBufferObj);
	void		DeleteSocketObj(TSocketObj* pSocketObj);

	void		AddClientSocketObj(DWORD dwConnID, TSocketObj* pSocketObj);
	TSocketObj* FindSocketObj(DWORD dwConnID);

private:
	static UINT WINAPI AcceptThreadProc(LPVOID pv);
	static UINT WINAPI WorkerThreadProc(LPVOID pv);

	void HandleIo		(TSocketObj* pSocketObj, TBufferObj* pBufferObj, DWORD dwBytes, DWORD dwErrorCode);
	void HandleAccept	(SOCKET soListen, TBufferObj* pBufferObj);
	void HandleSend		(TSocketObj* pSocketObj, TBufferObj* pBufferObj);
	void HandleReceive	(TSocketObj* pSocketObj, TBufferObj* pBufferObj);

	int DoSend		(TSocketObj* pSocketObj, TBufferObj* pBufferObj);
	int DoReceive	(TSocketObj* pSocketObj, TBufferObj* pBufferObj);

private:
	SOCKET	GetAcceptSocket();
	BOOL	DeleteAcceptSocket(SOCKET socket, BOOL bCloseSocket = FALSE);
	void	ReleaseAcceptSockets();

private:
	CInitSocket					m_wsSocket;
	LPFN_ACCEPTEX				m_pfnAcceptEx;
	LPFN_GETACCEPTEXSOCKADDRS	m_pfnGetAcceptExSockaddrs;
private:
	IServerSocketListener* m_psoListener;

	volatile BOOL	m_bStarted;
	volatile DWORD	m_dwConnID;

	En_ISS_Error	m_enLastError;

	SOCKET			m_soListen;
	HANDLE			m_hCompletePort;
	HANDLE			m_hAcceptThread;
	vector<HANDLE>	m_vtWorkerThreads;

	TBufferObjPtrList	m_lsFreeBuffer;
	TSocketObjPtrList	m_lsFreeSocket;
	TSocketObjPtrMap	m_mpClientSocket;

	CCriSec				m_csFreeBuffer;
	CCriSec				m_csFreeSocket;
	CCriSec				m_csClientSocket;

	CEvt				m_evtAccept;
	CSEM				m_semAccept;
	CCriSec				m_csAccept;
	ulong_set			m_setAccept;

	CPrivateHeap		m_hpPrivate;
};